from rest_framework.mixins import *
from rest_framework.generics import GenericAPIView
from django.db.models.query import QuerySet
from django.db import transaction, transaction

from user.models import UserProfile


class MultipleFieldLookupMixin(object):
	"""
	Apply this mixin to any view or viewset to get multiple field filtering
	based on a `lookup_fields` attribute, instead of the default single field filtering.
	"""
	def get_object(self):
		queryset = self.get_queryset()             # Get the base queryset
		queryset = self.filter_queryset(queryset)  # Apply any filter backends
		filter = {}
		for field in self.lookup_fields:
			if self.kwargs[field]: # Ignore empty fields.
				filter[field] = self.kwargs[field]
		obj = get_object_or_404(queryset, **filter)  # Lookup the object
		self.check_object_permissions(self.request, obj)
		return obj
		

class CustomCreateModelMixin(CreateModelMixin):

	def create(self, request, *args, **kwargs):
		serializer = self.get_serializer(data=request.data)
		if not serializer.is_valid():
			return Response({'status':'error', 'msg':serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
		model = getattr(serializer.Meta, 'model')
		if hasattr(model, 'level_set') and hasattr(model, 'exp_set') and hasattr(model, 'point_set'):
			self.perform_create_user(serializer)
		else:
			self.perform_create(serializer)
		headers = self.get_success_headers(serializer.data)
		return Response({'status':'ok', 'result':serializer.data}, status=status.HTTP_201_CREATED, headers=headers)

	def perform_create(self, serializer, **kwargs):
		serializer.save(**kwargs)

	def perform_create_user(self, serializer, **kwargs):
		with transaction.atomic():
			serializer.save(**kwargs)
			user = UserProfile.objects.get(username=serializer.data['username'])


class CustomListModelMixin(ListModelMixin):
	"""
	List a queryset.
	"""
	def list(self, request, *args, **kwargs):
		queryset = self.filter_queryset(self.get_queryset())
		page = self.paginate_queryset(queryset)
		if page is not None:
			serializer = self.get_serializer(page, many=True)
			return self.get_paginated_response({'status':'ok', 'results':serializer.data})

		serializer = self.get_serializer(queryset, many=True)
		return Response({'status':'ok', 'results':serializer.data})


class CustomRetrieveModelMixin(RetrieveModelMixin):
	"""
	Retrieve a model instance.
	"""
	def retrieve(self, request, *args, **kwargs):
		instance = self.get_object()
		serializer = self.get_serializer(instance)
		return Response({'status':'ok', 'result':serializer.data})


class CustomUpdateModelMixin(UpdateModelMixin):
	"""
	Update a model instance.
	"""
	def update(self, request, *args, **kwargs):
		partial = kwargs.pop('partial', False)
		ids = self.kwargs.get('pk').split(',')
		try:
			queryset = self.get_queryset().filter(id__in=ids)
		except:
			return Response({'status':'error', 'msg':'参数错误！'})
		with transaction.atomic():
			for instance in queryset:
				serializer = self.get_serializer(instance, data=request.data, partial=partial)
				if not serializer.is_valid():
					return Response({'status':'error', 'msg':serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
				self.perform_update(serializer)

				if getattr(instance, '_prefetched_objects_cache', None):
					# If 'prefetch_related' has been applied to a queryset, we need to
					# forcibly invalidate the prefetch cache on the instance.
					instance._prefetched_objects_cache = {}

			return Response({'status':'ok', 'msg':'批量修改成功！'})
		return Response({'status':'error', 'msg':'批量修改失败！'})


class CustomDestroyModelMixin(DestroyModelMixin):
	"""
	Destroy a model instance.
	"""
	def destroy(self, request, *args, **kwargs):
		ids = self.kwargs.get('pk').split(',')
		try:
			queryset = self.get_queryset().filter(id__in=ids)
		except:
			return Response({'status':'error', 'msg':'参数错误！'})
		with transaction.atomic():
			self.perform_destroy(queryset)
			return Response({'status':'ok'}, status=status.HTTP_204_NO_CONTENT)
		return Response({'status':'error', 'msg':'批量删除失败！'})


class CreateAPIView(CustomCreateModelMixin,
					GenericAPIView):
	"""
	Concrete view for creating a model instance.
	"""
	def post(self, request, *args, **kwargs):
		return self.create(request, *args, **kwargs)


class ListAPIView(CustomListModelMixin,
				  GenericAPIView):
	"""
	Concrete view for listing a queryset.
	"""
	def get(self, request, *args, **kwargs):
		return self.list(request, *args, **kwargs)


class RetrieveAPIView(CustomRetrieveModelMixin,
					  GenericAPIView):
	"""
	Concrete view for retrieving a model instance.
	"""
	def get(self, request, *args, **kwargs):
		return self.retrieve(request, *args, **kwargs)


class DestroyAPIView(CustomDestroyModelMixin,
					 GenericAPIView):
	"""
	Concrete view for deleting a model instance.
	"""
	def delete(self, request, *args, **kwargs):
		return self.destroy(request, *args, **kwargs)


class UpdateAPIView(CustomUpdateModelMixin,
					GenericAPIView):
	"""
	Concrete view for updating a model instance.
	"""
	def put(self, request, *args, **kwargs):
		return self.update(request, *args, **kwargs)

	def patch(self, request, *args, **kwargs):
		return self.partial_update(request, *args, **kwargs)


class ListCreateUpdateAPIView(CustomListModelMixin,
						CustomCreateModelMixin,
						CustomUpdateModelMixin,
						GenericAPIView):
	"""
	Concrete view for listing a queryset or creating a model instance.
	"""
	def get(self, request, *args, **kwargs):
		return self.list(request, *args, **kwargs)

	def post(self, request, *args, **kwargs):
		return self.create(request, *args, **kwargs)

	def put(self, request, *args, **kwargs):
		return self.update(request, *args, **kwargs)

	def patch(self, request, *args, **kwargs):
		return self.partial_update(request, *args, **kwargs)


class ListUpdateAPIView(CustomListModelMixin,
						CustomUpdateModelMixin,
						GenericAPIView):
	"""
	Concrete view for listing a queryset or creating a model instance.
	"""
	def get(self, request, *args, **kwargs):
		return self.list(request, *args, **kwargs)

	def put(self, request, *args, **kwargs):
		return self.update(request, *args, **kwargs)

	def patch(self, request, *args, **kwargs):
		return self.partial_update(request, *args, **kwargs)


class ListCreateAPIView(CustomListModelMixin,
						CustomCreateModelMixin,
						GenericAPIView):
	"""
	Concrete view for listing a queryset or creating a model instance.
	"""
	def get(self, request, *args, **kwargs):
		return self.list(request, *args, **kwargs)

	def post(self, request, *args, **kwargs):
		return self.create(request, *args, **kwargs)


class RetrieveUpdateAPIView(CustomRetrieveModelMixin,
							CustomUpdateModelMixin,
							GenericAPIView):
	"""
	Concrete view for retrieving, updating a model instance.
	"""
	def get(self, request, *args, **kwargs):
		return self.retrieve(request, *args, **kwargs)

	def put(self, request, *args, **kwargs):
		return self.update(request, *args, **kwargs)

	def patch(self, request, *args, **kwargs):
		return self.partial_update(request, *args, **kwargs)


class RetrieveDestroyAPIView(CustomRetrieveModelMixin,
							 CustomDestroyModelMixin,
							 GenericAPIView):
	"""
	Concrete view for retrieving or deleting a model instance.
	"""
	def get(self, request, *args, **kwargs):
		return self.retrieve(request, *args, **kwargs)

	def delete(self, request, *args, **kwargs):
		return self.destroy(request, *args, **kwargs)


class RetrieveUpdateDestroyAPIView(CustomRetrieveModelMixin,
								   CustomUpdateModelMixin,
								   CustomDestroyModelMixin,
								   GenericAPIView):
	"""
	Concrete view for retrieving, updating or deleting a model instance.
	"""
	def get(self, request, *args, **kwargs):
		return self.retrieve(request, *args, **kwargs)

	def put(self, request, *args, **kwargs):
		return self.update(request, *args, **kwargs)

	def patch(self, request, *args, **kwargs):
		return self.partial_update(request, *args, **kwargs)

	def delete(self, request, *args, **kwargs):
		return self.destroy(request, *args, **kwargs)